function hfile = vmp_Update(hfile, F, S, V)
% VMP::Update  - called after subsasgn for VMPs

% Version:  v0.7f
% Build:    8120122
% Date:     Dec-01 2008, 10:34 PM CET
% Author:   Jochen Weber, SCAN Unit, Columbia University, NYC, NY, USA
% URL/Info: http://wiki.brainvoyager.com/BVQXtools

% global settings
global bvqxfile_config;

% persistent config
persistent bvqxfile_vmp;
if isempty(bvqxfile_vmp)
    bvqxfile_vmp = struct;
    
    bvqxfile_vmp.fdr = bvqxfile_config.settings.Statistics.FDR.Thresholds;

    bvqxfile_vmp.fields = cell(1, 6);
    bvqxfile_vmp.fields{2} = ...
        {'NativeResolutionFile', 'FileVersion', 'NrOfMaps', 'Map', ...
         'VMRDimX', 'VMRDimY', 'VMRDimZ', ...
         'XStart', 'XEnd', 'YStart', 'YEnd', 'ZStart', 'ZEnd', ...
         'Resolution'};
    bvqxfile_vmp.fields{3} = bvqxfile_vmp.fields{2};
    bvqxfile_vmp.fields{4} = bvqxfile_vmp.fields{2};
    bvqxfile_vmp.fields{5} = ...
        {'NativeResolutionFile', 'FileVersion', 'DocumentType', ...
         'NrOfMaps', 'NrOfTimePoints', 'NrOfMapParameters', ...
         'ShowParamsRangeFrom', 'ShowParamsRangeTo', ...
         'FingerprintParamsRangeFrom', 'FingerprintParamsRangeTo', ...
         'XStart', 'XEnd', 'YStart', 'YEnd', 'ZStart', 'ZEnd', 'Resolution', ...
         'DimX', 'DimY', 'DimZ', 'VMRData', ...
         'VMRDimX', 'VMRDimY', 'VMRDimZ', ...
         'OriginatingVTC', 'LinkedPRT', 'OptionalVOI', ...
         'Map', 'MapParameter'};
    bvqxfile_vmp.fields{6} = bvqxfile_vmp.fields{5};

    bvqxfile_vmp.map_fields = cell(1, 6);
    bvqxfile_vmp.map_fields{2} = ...
        {'Type', 'NrOfLags', 'MinLag', 'MaxLag', 'CCOverlay', ...
         'ClusterSize', 'EnableClusterCheck', ...
         'LowerThreshold', 'UpperThreshold', 'UseValuesAboveThresh', ...
         'DF1', 'DF2', 'ShowPositiveNegativeFlag', 'BonferroniValue', ...
         'RGBLowerThreshPos', 'RGBUpperThreshPos', ...
         'RGBLowerThreshNeg', 'RGBUpperThreshNeg', ...
         'UseRGBColor', 'LUTName', 'TransColorFactor', 'Name', 'VMPData'};
    bvqxfile_vmp.map_fields{3} = bvqxfile_vmp.map_fields{2};
    bvqxfile_vmp.map_fields{4} = bvqxfile_vmp.map_fields{2};
    bvqxfile_vmp.map_fields{5} = ...
        {'Type', 'LowerThreshold', 'UpperThreshold', 'Name', ...
         'RGBLowerThreshPos', 'RGBUpperThreshPos', ...
         'RGBLowerThreshNeg', 'RGBUpperThreshNeg', ...
         'UseRGBColor', 'LUTName', 'TransColorFactor', ...
         'NrOfLags', 'MinLag', 'MaxLag', 'CCOverlay', ...
         'ClusterSize', 'EnableClusterCheck', ...
         'LowerThreshold', 'LowerThreshold', 'UseValuesAboveThresh', ...
         'DF1', 'DF2', 'ShowPositiveNegativeFlag', 'BonferroniValue', ...
         'NrOfFDRThresholds', 'FDRThresholds', ...
         'UnknownValue', 'TimePointData', 'VMPData'};
    bvqxfile_vmp.map_fields{6} = bvqxfile_vmp.map_fields{5};

    bvqxfile_vmp.defaults = {[], [], [], struct( ...
        'NativeResolutionFile', 0, ...
        'FileVersion',          4, ...
        'NrOfMaps',             0, ...
        'Map',                  cell2struct(cell(0, 0, 23), ...
            {'Type', 'NrOfLags', 'MinLag', 'MaxLag', 'CCOverlay', ...
             'ClusterSize', 'EnableClusterCheck', ...
             'LowerThreshold', 'UpperThreshold', 'UseValuesAboveThresh', ...
             'DF1', 'DF2', 'ShowPositiveNegativeFlag', 'BonferroniValue', ...
             'RGBLowerThreshPos', 'RGBUpperThreshPos', ...
             'RGBLowerThreshNeg', 'RGBUpperThreshNeg', ...
             'UseRGBColor', 'LUTName', 'TransColorFactor', 'Name', 'VMPData'}, 3), ...
        'VMRDimX',              256, ...
        'VMRDimY',              256, ...
        'VMRDimZ',              256, ...
        'XStart',               57, ...
        'XEnd',                 231, ...
        'YStart',               52, ...
        'YEnd',                 172, ...
        'ZStart',               59, ...
        'ZEnd',                 197, ...
        'Resolution',           3 ...
      ), [], struct( ...
        'NativeResolutionFile',         1, ...
        'FileVersion',                  5, ...
        'DocumentType',                 1, ...
        'NrOfMaps',                     0, ...
        'NrOfTimePoints',               0, ...
    	'NrOfMapParameters',            0, ...
        'ShowParamsRangeFrom',          0, ...
        'ShowParamsRangeTo',            0, ...
        'FingerprintParamsRangeFrom',   0, ...
        'FingerprintParamsRangeTo',     0, ...
        'XStart',                       57, ...
        'XEnd',                         231, ...
        'YStart',                       52, ...
        'YEnd',                         172, ...
        'ZStart',                       59, ...
    	'ZEnd',                         197, ...
        'Resolution',                   3, ...
        'VMRDimX',                      256, ...
        'VMRDimY',                      256, ...
        'VMRDimZ',                      256, ...
        'OriginatingVTC',               '', ...
        'LinkedPRT',                    '', ...
        'OptionalVOI',                  '', ...
        'Map',                          cell2struct(cell(0, 0, 27), ...
            {'Type', 'LowerThreshold', 'UpperThreshold', 'Name', ...
             'RGBLowerThreshPos', 'RGBUpperThreshPos', ...
             'RGBLowerThreshNeg', 'RGBUpperThreshNeg', ...
             'UseRGBColor', 'LUTName', 'TransColorFactor', ...
             'NrOfLags', 'MinLag', 'MaxLag', 'CCOverlay', ...
             'ClusterSize', 'EnableClusterCheck', 'UseValuesAboveThresh', ...
             'DF1', 'DF2', 'ShowPositiveNegativeFlag', 'BonferroniValue', ...
             'NrOfFDRThresholds', 'FDRThresholds', ...
             'UnknownValue', 'TimePointData', 'VMPData'}, 3), ...
        'MapParameter',                 cell2struct(cell(0, 0, 2), ...
            {'Name', 'Values'}, 3) ...
    )};
    bvqxfile_vmp.defaults{2} = bvqxfile_vmp.defaults{4};
    bvqxfile_vmp.defaults{3} = bvqxfile_vmp.defaults{4};
    bvqxfile_vmp.defaults{5} = bvqxfile_vmp.defaults{6};
    
    % improve defaults
    for vc = 2:6
        bvqxfile_vmp.defaults{vc}.FileVersion = vc;
        bvqxfile_vmp.defaults{vc}.Map(1).ShowPositiveNegativeFlag = 3;
    end
    
end

% argument check
if nargin < 2 || ...
    numel(hfile) ~= 1 || ...
   ~isBVQXfile(hfile(:), 'vmp') || ...
   ~ischar(F) || ...
    isempty(F)
    error( ...
        'BVQXfile:BadArgument', ...
        'Invalid call to %s.', ...
        mfilename ...
    );
end
if nargin < 3 || ...
   ~isstruct(S)
    S = struct;
    S.type = '.';
    S.subs = F;
end
if nargin < 4
    V = [];
end

% get content
bc = bvqxfile_getcont(hfile.L);

% linearize
F = makelabel(F(:)');

% F valid
if ~isfield(bc, F)
    error( ...
        'BVQXfile:InvalidProperty', ...
        'Cannot find property ''%s'' for type VMP.', ...
        F ...
    );
end

% what field has changed
switch (lower(F))

    % changing file version
    case {'fileversion'}

        % get wanted and old version
        reqv = bc.FileVersion;
        oldv = V;

        % check version
        if isempty(reqv) || ...
           ~isa(reqv, 'double') || ...
            isnan(reqv(1)) || ...
            isinf(reqv(1)) || ...
            fix(reqv(1)) ~= reqv(1) || ...
            reqv(1) < 2 || ...
            reqv(1) > 6 || ...
            isempty(oldv) || ...
           ~isa(oldv, 'double') || ...
            isnan(oldv(1)) || ...
            isinf(oldv(1)) || ...
            fix(oldv(1)) ~= oldv(1) || ...
            oldv(1) < 2 || ...
            oldv(1) > 6

            % give warning if required
            warning( ...
                'BVQXfile:InvalidPropertyValue', ...
                'Invalid old/new FileVersion value.' ...
            );

            % guess better values
            reqv = 5;
            bc.FileVersion = reqv;
            if bc.NativeResolutionFile
                oldv = 5;
            else
                oldv = 4;
            end
        end
        
        % if resolution is 1 make sure to go to old format
        if bc.Resolution == 1
            reqv = 4;
        end

        % something to do?
        if (oldv < 5 && reqv < 5) || ...
           (oldv > 4 && reqv > 4)
            bvqxfile_setcont(hfile.L, bc);
            return;
        end
        
        % build new content
        newCONT = bvqxfile_vmp.defaults{reqv};
        oldMap  = bc.Map;
        newMap  = newCONT.Map;

        % upgrade
        if oldv < 5
            
            % set global values
            newCONT.NativeResolutionFile = 1;
            newCONT.NrOfMaps   = numel(oldMap);
            newCONT.XStart     = bc.XStart;
            newCONT.XEnd       = bc.XEnd;
            newCONT.YStart     = bc.YStart;
            newCONT.YEnd       = bc.YEnd;
            newCONT.ZStart     = bc.ZStart;
            newCONT.ZEnd       = bc.ZEnd;
            newCONT.Resolution = bc.Resolution;
            dimres = newCONT.Resolution;
            dimX = (newCONT.XEnd - newCONT.XStart) / dimres;
            dimY = (newCONT.YEnd - newCONT.YStart) / dimres;
            dimZ = (newCONT.ZEnd - newCONT.ZStart) / dimres;
            if any([dimX, dimY, dimZ] ~= fix([dimX, dimY, dimZ]))
                error( ...
                    'BVQXfile:DimensionError', ...
                    'X/Y/Z Start/End values must match with Resolution.' ...
                );
            end
            
            if newCONT.NrOfMaps > 0
                newres = newCONT.Resolution;
                oldres = [ ...
                    (newCONT.XEnd - newCONT.XStart) / size(oldMap(1).VMPData, 1), ...
                    (newCONT.YEnd - newCONT.YStart) / size(oldMap(1).VMPData, 2), ...
                    (newCONT.ZEnd - newCONT.ZStart) / size(oldMap(1).VMPData, 3)];
                if any(oldres ~= fix(oldres))
                    oldres = [ ...
                        (1 + newCONT.XEnd - newCONT.XStart) / size(oldMap(1).VMPData, 1), ...
                        (1 + newCONT.YEnd - newCONT.YStart) / size(oldMap(1).VMPData, 2), ...
                        (1 + newCONT.ZEnd - newCONT.ZStart) / size(oldMap(1).VMPData, 3)];
                end
                if any(diff(oldres)) || ...
                    any(oldres ~= fix(oldres))
                    error( ...
                        'BVQXfile:DimensionError', ...
                        'Map dimension mismatch with in X/Y/Z Start/End.' ...
                    );
                end
                oldres = oldres(1);
                difres = newres / oldres;
                if difres ~= fix(difres)
                    scrds = 0.01 + (1:difres:(difres * (max([dimX, dimY, dimZ]) + 1)));
                end
            end
            
            % iterate over maps
            for mc = 1:numel(oldMap)
                
                % set values
                newMap(mc).Type               = oldMap(mc).Type;
                newMap(mc).LowerThreshold     = oldMap(mc).LowerThreshold;
                newMap(mc).UpperThreshold     = oldMap(mc).UpperThreshold;
                newMap(mc).Name               = oldMap(mc).Name;
                newMap(mc).RGBLowerThreshPos  = oldMap(mc).RGBLowerThreshPos;
                newMap(mc).RGBUpperThreshPos  = oldMap(mc).RGBUpperThreshPos;
                newMap(mc).RGBLowerThreshNeg  = oldMap(mc).RGBLowerThreshNeg;
                newMap(mc).RGBUpperThreshNeg  = oldMap(mc).RGBUpperThreshNeg;
                newMap(mc).UseRGBColor        = oldMap(mc).UseRGBColor;
                newMap(mc).LUTName            = oldMap(mc).LUTName;
                newMap(mc).TransColorFactor   = oldMap(mc).TransColorFactor;
                newMap(mc).NrOfLags           = oldMap(mc).NrOfLags;
                newMap(mc).MinLag             = oldMap(mc).MinLag;
                newMap(mc).MaxLag             = oldMap(mc).MaxLag;
                newMap(mc).CCOverlay          = oldMap(mc).CCOverlay;
                newMap(mc).ClusterSize        = ...
                    round(oldMap(mc).ClusterSize / difres ^ 3);
                newMap(mc).EnableClusterCheck = oldMap(mc).EnableClusterCheck;
                newMap(mc).UseValuesAboveThresh = ...
                    oldMap(mc).UseValuesAboveThresh;
                newMap(mc).DF1                = oldMap(mc).DF1;
                newMap(mc).DF2                = oldMap(mc).DF2;
                newMap(mc).ShowPositiveNegativeFlag = 3;
                newMap(mc).BonferroniValue    = oldMap(mc).BonferroniValue;
                newMap(mc).NrOfFDRThresholds  = numel(bvqxfile_vmp.fdr);
                newMap(mc).UnknownValue       = -1;
                newMap(mc).TimePointData      = zeros(0, 1);
                if difres == fix(difres)
                    newMap(mc).VMPData        = ...
                        oldMap(mc).VMPData(1:difres:end-1, 1:difres:end-1, 1:difres:end-1);
                else
                    newMap(mc).VMPData        = ...
                        flexinterpn(oldMap(mc).VMPData, ...
                            [Inf, Inf, Inf; 1, 1, 1; difres, difres, difres; ...
                             scrds(dimX), scrds(dimY), scrds(dimZ)], [0;1;0], 1, 0);
                end
                
                % build FDR table
                try
                    mvals = newMap(mc).VMPData( ...
                        ~isnan(newMap(mc).VMPData(:)) & ...
                        (newMap(mc).VMPData(:) ~= 0));
                    if ~isempty(mvals)
                        switch (newMap(mc).Type)
                            case {1}  % t-score
                                newMap(mc).FDRThresholds = ...
                                    [bvqxfile_vmp.fdr(:), ...
                                    applyfdr(double(mvals), 't', ...
                                    bvqxfile_vmp.fdr(:), newMap(mc).DF1, ...
                                    [], true)];
                            case {2}  % correlation
                                newMap(mc).FDRThresholds = ...
                                    [bvqxfile_vmp.fdr(:), ...
                                    applyfdr(double(mvals), 'r', ...
                                    bvqxfile_vmp.fdr(:), newMap(mc).DF1, ...
                                    [], true)];
                            case {4}  % F-score
                                newMap(mc).FDRThresholds = ...
                                    [bvqxfile_vmp.fdr(:), ...
                                    applyfdr(double(mvals), 'F', ...
                                    bvqxfile_vmp.fdr(:), newMap(mc).DF1, ...
                                    newMap(mc).DF2, true)];
                            otherwise
                                error('FDR_ERROR');
                        end
                    else
                        newMap(mc).NrOfFDRThresholds = 1;
                        newMap(mc).FDRThresholds = [0, 1e5, 1e5];
                    end
                catch
                    newMap(mc).NrOfFDRThresholds = 1;
                    newMap(mc).FDRThresholds = [0, 1e5, 1e5];
                end
            end
            
            % put Map into new object
            newCONT.Map = newMap;
            
        % downgrade
        else
            
            try
                nhfile = vmp_MakeHiResVMP(hfile);
            catch
                warning( ...
                    'BVQXfile:ConversionError', ...
                    'Error converting NatRes to HiRes VMP.' ...
                );
                return;
            end
            newCONT = bvqxfile_getcont(nhfile.L);
            aft_ClearObject(nhfile);
            newCONT.FileVersion = reqv;
        end
        
        % put back into object
        bc = newCONT;
end

% set back
bvqxfile_setcont(hfile.L, bc);
